#pragma once

#include "noncopyable.h"
#include "TcpConnection.h"
#include <mutex>

class TcpClient
{
public:
    TcpClient(EventLoop* loop, const string& ip, const int port);
    ~TcpClient();

    void connect();
    void disconnect();
    void stop();
    void start();

    TcpConnectionPtr connection() const
    {
        std::lock_guard<std::mutex> lock(mutex_);
        return connection_;
    }

    EventLoop* getLoop() const { return loop_; }
    bool retry() const { return retry_; }
    void enableRetry() { retry_ = true; }

    void setConnectionCallback(const ConnectionCallback& cb){ connectionCallback_ = cb; }
    void setMessageCallback(const MessageCallback& cb){ messageCallback_ = cb; }
    void setWriteCompleteCallback(const WriteCompleteCallback& cb){ writeCompleteCallback_ = cb; }


private:
    enum States { kDisconnected, kConnecting, kConnected };
    static const int kMaxRetryDelayMs = 20*1000;
    static const int kInitRetryDelayMs = 1000;

    void setState(States s) { state_ = s; }

    void connectServer();
    void connecting(int sockfd);
    void retry(int sockfd);
    void stopInLoop();
    void startInLoop();
    void restart();
    void resetChannel();
    int removeAndResetChannel();

    void handleWrite();
    void handleError();

    void newConnection(int sockfd);
    void removeConnection(const TcpConnectionPtr& conn);

    EventLoop* loop_;
    const string name_;
    ConnectionCallback connectionCallback_;
    MessageCallback messageCallback_;
    WriteCompleteCallback writeCompleteCallback_;

    bool retry_;
    bool connect_;
    int nextConnId_;
    mutable std::mutex mutex_;
    TcpConnectionPtr connection_;

    const char* host_;
    int port_;
    std::shared_ptr<Channel> channel_;
    int retryDelayMs_;
    States state_;
};